package com.skp.shaphan.ui.results;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;

import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;

/**
 * A pane that can display a ResultSet in a grid.  Contains a toolbar and other
 * UI to aide in working with that resultset.
 * @author Stephen Phillips
 *
 */
public class ResultPane extends JPanel implements AdjustmentListener, IResultPane {
	/**
	 * 
	 */
	private static final long serialVersionUID = 3836516424057259458L;
	private ResultsTable table;
	private ResultsStatusBar statusBar;
	private String currentSQLStatement = "";
	private JTextPane messages;
	
	public ResultPane() {
		this.setLayout(new GridBagLayout());
		this.setPreferredSize(new Dimension(400, 200));
		table = new ResultsTable();
		JScrollPane scroll = new JScrollPane(table);
		scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		scroll.getVerticalScrollBar().addAdjustmentListener(this);
		table.setFillsViewportHeight(true);
		table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		statusBar = new ResultsStatusBar();
		
		JTabbedPane tabs = new JTabbedPane();
		tabs.add("Data", scroll);
		
		messages = new JTextPane();
		scroll = new JScrollPane(messages);
		scroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		tabs.add("Messages", scroll);
		
		this.add(new ResultsToolbar(), new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 1, 0, 0), 5, 5));
		this.add(tabs, new GridBagConstraints(0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(5, 1, 5, 0), 5, 5));
		this.add(statusBar, new GridBagConstraints(0, 3, 1, 1, 1.0, 0.0, GridBagConstraints.SOUTH, GridBagConstraints.HORIZONTAL, new Insets(0, 1, 0, 0), 5, 5));
	}

	@Override
	public void setResultSet(String sqlStatement, ResultSet results) {
		if(results != null) {
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					setStatusMessage("reading data...");
				}
			});
		}

		currentSQLStatement = sqlStatement;
		table.setResultSet(results);

		if(results != null) {
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					String finishedLoadingIndicator = "";
					if(!isFinishedLoading()) {
						finishedLoadingIndicator = "+";
					}
					setStatusMessage(getRowsLoaded() + finishedLoadingIndicator + " rows");
				}
			});
		}
	}
	
	@Override
	public ResultSet getResultSet() {
		return table.getResultSet();
	}
	
	@Override
	public void setStatusMessage(String message) {
		statusBar.setStatusMessage(message);
	}

	public int getRowsLoaded() {
		return ((ResultsModel)table.getModel()).getRowsLoaded();
	}

	public boolean isFinishedLoading() {
		return ((ResultsModel)table.getModel()).isFinishedLoading();
	}

	/**
	 * Designed to load more rows when the user scrolls to the bottom
	 */
	@Override
	public void adjustmentValueChanged(AdjustmentEvent e) {
		ResultsModel model = (ResultsModel)table.getModel(); 
		if(!model.hasResults() || model.isFinishedLoading()) {
			return;
		}
		JScrollBar scrollbar = (JScrollBar)e.getSource();
		int percent = (int)(((float)(e.getValue() + scrollbar.getModel().getExtent())) / (float)scrollbar.getMaximum() * 100);
		if(percent >= 90) {
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					setStatusMessage("reading data...");
				}
			});

			((ResultsModel)table.getModel()).loadMoreRows();

			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					String finishedLoadingIndicator = "";
					if(!isFinishedLoading()) {
						finishedLoadingIndicator = "+";
					}
					setStatusMessage(getRowsLoaded() + finishedLoadingIndicator + " rows");
				}
			});
		}
	}

	@Override
	public String getCurrentSQLStatement() {
		return currentSQLStatement;
	}

	@Override
	public void clearMessages() {
		messages.setText("");
	}
	
	@Override
	public void setSQLMessages(ResultSet rs, Statement st, SQLException error) {
		StringWriter sw = new StringWriter();
		PrintWriter msg = new PrintWriter(sw);
		if(messages.getText().length() > 0) {
			msg.println(messages.getText());
		}
		if(error != null) {
			msg.println("Error: " + error.getLocalizedMessage());
			msg.println("Detail: " + error.getMessage());
			error.printStackTrace(msg);
		}
		if(rs != null) {
			try {
				SQLWarning warning = rs.getWarnings();
				addSQLMessages(msg, warning);
			} catch (SQLException e) {
				msg.println("");
				msg.println("Error reading results warnings: " + e.getLocalizedMessage());
				e.printStackTrace(msg);
			}
		}
		if(st != null) {
			try {
				SQLWarning warning = st.getWarnings();
				addSQLMessages(msg, warning);
				warning = st.getConnection().getWarnings();
				addSQLMessages(msg, warning);
			} catch (SQLException e) {
				msg.println("");
				msg.println("Error reading statement warnings: " + e.getLocalizedMessage());
				e.printStackTrace(msg);
			}
		}
		
		messages.setText(sw.toString());
	}
	
	private void addSQLMessages(PrintWriter msg, SQLWarning warning) {
		while(warning != null) {
			msg.print("Warning -- Error Code; ");
			msg.print(warning.getErrorCode());
			msg.print("   ");
			msg.println(warning.getLocalizedMessage());
			warning = warning.getNextWarning();
		}
	}
}
